package SSF.App.DDoS;

import com.renesys.raceway.DML.*;
import com.renesys.raceway.SSF.*;
import java.util.*;

/**
 * DDoS Session, the attacker chooses masters and agents randomly.
 * <P>In this version, the user just specify the attacker and how many children
 * does each non-leaf node control. The attacker chooses hosts to be hijacked
 * randomly. Notice that there are at most two levels of control:<BR>
 *   attacker --> master --> agent<BR>
 * so there is no need to specify the total number of hosts to be hijacked.
 * If there are not enough hosts to be hijacked, the node will be assigned in
 * a depth-first order.
 * <P>For those who want to have total control of the attacking relay network
 * topology, please use DDoSSession. But please also keep in mind that it does
 * not has as many features as this one. 
 *
 * This is a revised version, an option to use spoofed packets among agent, 
 * masters and the attacker is added. 
 * @author Yougu Yuan
 * @see DDoSSession
 */
public class DDoSSessionRand extends ProtocolSession
{    /** debug switch */
    private static boolean DEBUG;

    /** status constants, so are "INSTALLED" and "ATTACKING" */
    public static final int CLEAN = -2;
    public static final int COMPROMISED = 0;
    public static final int INSTALLED = 1;
    public static final int ATTACKING = 2;


    /** level constants, so are "MASTER" and "AGENT"*/
    public static final int ATTACKER = 0;
    public static final int MASTER = 1;
    public static final int AGENT = 2;

    /** virtual interface that is assumed to be on at every host*/
    public static final String VIRTUAL_INTERFACE = "(0)";

    /** attack method constant */
    public static final int SYN=0;

    /** status variable, default is "INSTALLED"*/
    public int status = CLEAN;

    /** whether to use spoofed packets among zombies, masters and attacker*/
    public static boolean spoof = false;

    /** monitors for DDoSSession */
    protected ProtocolMonitor monitor = null;

    /** monitor switch */
    protected boolean monitorON = false;

    /** level variable, it shows where in the control tree this host locates.*/
    protected int level = AGENT;

    /** the degree of this tree */
    protected static int degree = 1;

    /** the time when the attacker "hijack" hosts */
    protected static int startHijackTime = 80;

    /** attack starting time (second). It must be initialized for ATTACKER*/
    protected int startAttackTime = 100;

    /** attack ending time (second). It must be initialized for ATTACKER*/
    protected int endAttackTime = 200;

    /** target IP */
    protected int targetIP;

    /** target port*/
    protected int targetPort=80;

    /** attack method*/
    protected int attackMethod=SYN;

    /** parameter of attacking */
    protected int attackParam = 1;

    /** array of IP addresses of children that are directly under control 
	of this master or attacker */
    protected int[] children = null;

    /** IP on this host */
    protected IP ipSess=null;

    /** control tree config file */
    protected static String controlTreeFile=null;
    protected static Configuration controlTreeConfig=null;

    /** timer */
    protected Timer myTimer=null;

    /** random stream */
    protected static RandomStream rng=null;

    /** config the status and level of this DDoSSession instance.
     * <P>
     * <PRE>
     * ProtocolSession [
     *     name test use SSF.OS.DDoS.DDoSSessionRand
     *     debug %boolean           #debug switch
     *     control_tree_file %s     #control tree file name
     *     monitor [
     *         use SSF.OS.DDoSMonitor
     *         debug %s       #debug switch, true/false
     *     ]
     * ]
     * </PRE>
     **/
    @Override
	public void config(Configuration cfg) throws configException
    {
	super.config(cfg);

	String evals = null;

	/** config monitor, if any */
	Configuration mcfg = (Configuration)cfg.findSingle("monitor");	    
	if (mcfg != null)
	    createMonitor(mcfg);

	//config the static controlTreeFile if it's not configed yet.
	if (controlTreeFile == null) {
	    evals = (String)cfg.findSingle("debug");
	    if (evals != null) {
		if (evals.equals("true"))
		    DEBUG = true;
	    }

	    evals = (String)cfg.findSingle("control_tree_file");
	    if (evals == null) {
		System.err.println("control_tree_file attribute is not found for the DDoSSession.");
		throw new configException("control_tree_file attribute not found.");
	    }
	    else controlTreeFile = evals;

	    // get the configuration
	    dmlConfig controlConfig = new dmlConfig(controlTreeFile);
	    controlTreeConfig = 
		(Configuration)controlConfig.findSingle("control_tree");

	    if (controlTreeConfig == null) {
		System.err.println("Control Tree Configuration database must define a control_tree attribute.");
		System.err.println(controlConfig);
		throw new configException("control_tree attribute not found in the configuration file of control tree.");
	    }
	}

	//create randomstream if it's not there yet.
	if (rng == null) {
	    rng = Net.accessRandomStream(this, "DDoS");
	}
    }

    /**
     * Init the config tree. Only the *attacker* node needs to be initialized.
     * <UL>
     * <LI> status -- the init status.
     * <LI> degree -- the degree of each node.
     * <LI> monitor 
     * </UL>
     */
    @Override
	public void init() throws ProtocolException
    {
	String evals = null;
	Configuration localCfg = null;

	// init monitor if needed.
	if (monitorON)
	    monitor.init();

	try {
	    localCfg = (Configuration)
		controlTreeConfig.findSingle((((Host)(inGraph()))).nhi);

	    if (localCfg == null) {
		return;  //it doesn't need init.
		// or the *real* init work will be invoked by higher level node
	    }

	    //only the attacker is inited in the control_tree_file.
	    //config specific to attacker.
	    initAttacker(localCfg);

	} catch (configException ce) {
	    throw new ProtocolException("Error in initialization of DDoSSession");
	}
    }


    protected void initStatus(String evals) throws configException
    {
	//nothing is needed in this kind of DDoSSession
	//because DDoS hosts are randomly chosen.
    }

    protected void initLevel(String evals) throws configException
    {
	//nothing is needed in this kind of DDoSSession
	//because DDoS hosts are randomly chosen.
    }

    /**
     * Init the attacker.
     * <PRE>
     * attacker_nhi [
     *     degree %d             #number of children of each non-leaf node
     *     start_hijack_time %d  #time to issue "hijack" msg
     *     start_attack_time %d  #time to start attack
     *     end_attack_time   %d  #time to end attack
     *     target_nhi        %s  #nhi of the target
     *     target_port       %d  #target port number
     *     attack_method     %s  #currently only "SYN" is implemented
     *     param             %d  #parameter specific to attack
     *     spoof       %boolean  #whether to spoof communications
     * ]
     * </PRE>
     */
    protected void initAttacker(Configuration cfg) throws configException
    {
	String evals = null;

	//get local IP. IP must be configed by now, otherwise may be mistaken!!
	try {
	    ipSess = (IP)(inGraph().SessionForName("ip"));
	} catch (ProtocolException pe) {
	    System.err.println("IP not found in the attacker "
			       +((Host)inGraph()).nhi);
	    System.exit(-1);
	}

	if (DEBUG)
	    System.out.println("Init Attacker "+((Host)inGraph()).nhi+"\tIP:"
			       +IP_s.IPtoString(ipSess.INTERFACE_SET[0].ipAddr));

	//init status and level
	status = INSTALLED;
	level = ATTACKER;

	//init degree 
	evals = (String)cfg.findSingle("degree");
	if (evals != null)
	    degree = Integer.valueOf(evals).intValue();

	//default is 80
	evals = (String)cfg.findSingle("start_hijack_time");
	if (evals != null)
	    startHijackTime = Integer.valueOf(evals).intValue();

	//default is 100
	evals = (String)cfg.findSingle("start_attack_time");
	if (evals != null) 
	    startAttackTime = Integer.valueOf(evals).intValue();
	
	//default is 200
	evals = (String)cfg.findSingle("end_attack_time");
	if (evals != null)
	    endAttackTime = Integer.valueOf(evals).intValue();

	//get target ip
	evals = (String)cfg.findSingle("target_nhi");
	if (evals !=null) {
	    evals = ((Host)inGraph()).getNet().nhi_to_ip(evals+VIRTUAL_INTERFACE);
	    if (evals ==null) {
		System.err.println("Invalid target_nhi address.");
		throw new configException("Invalid target_nhi address.");
	    }
	    else targetIP = IP_s.StrToInt(evals);
	}
	else {
	    System.err.println("No target_ip specified for the attacker.");
	    throw new configException("No target_ip specified.");
	}

	//get port number, default is 80
	evals = (String)cfg.findSingle("target_port");
	if (evals != null)
	    targetPort = Integer.valueOf(evals).intValue();


	// get attack method
	evals = (String)cfg.findSingle("attack_method");
	if (evals != null) {
	    if (evals.equals("SYN"))
		attackMethod = SYN;
	    else {
		System.err.println("Attacking method other than \"SYN\" hasn't been implemented.");
		throw new configException("Attacking method not implemented.");
	    }
	}
      
	// get parameter if needed.
	if (attackMethod == SYN) {
	    evals = (String)cfg.findSingle("param");
	    if (evals != null)
		attackParam = Integer.valueOf(evals).intValue();
	}

	// check whether use spoof option
	evals = (String)cfg.findSingle("spoof");
	if (evals != null) {
	    if (evals.equals("true"))
		spoof = true;
	}


	//construct a host list.
	LinkedList hostList = new LinkedList();
	for (Enumeration tempEnum =((Host)inGraph()).getNet().hosts.elements();
	     tempEnum.hasMoreElements();)
	    hostList.add(tempEnum.nextElement());

	//remove the attacker itself
	hostList.remove((inGraph()));

	//choose masters and initialize them
	try {
	    constructChildrenList(hostList);
	} catch (ProtocolException pe) {
	    throw new configException(pe.getMessage());
	}

	//set a timer to fire at the startHijackTime
	myTimer = new Timer(inGraph(), SSF.Net.Net.seconds(startHijackTime)) {
		@Override
		public void callback() {
		    startHijack(children);
		}
	    };
	myTimer.set();
    }
    /** Instantiate and configure an monitor if specified */
    private void createMonitor(Configuration config) throws configException
    {
	String monitor_type = (String)config.findSingle("use");
	
	if (monitor_type == null)
	    throw new configException("\n"+debugIdentifier()+" DML monitor.use is not specified");

	try {
	    Class mClass = Class.forName(monitor_type);
	    Object mobj = mClass.newInstance();
	    monitor = (ProtocolMonitor)mobj;
	}
	catch (Exception any) {
	    System.err.println(debugIdentifier()+" Can't create an instance of "+monitor_type);
	    any.printStackTrace();
	    throw new configException("Can't create monitor "+monitor_type);
	}

	//let the monitor do its own config, and let it know the owner IP session
	monitor.config(this, config);

	//turn on the monitor
	setMonitorEnable(true);
    }
    /** An ProtocolMonitor may turn on and off calls to its receive()
     *  method. NOTE: the ProtocolMonitor MUST explicitely set
     *  enableMonitor(true) in its init() method to begin
     *  receiving IP packets.
     */
    public void setMonitorEnable(boolean en) {
	if(monitor != null) monitorON = en;
    }
    /**
     * @ret the number of hosts left in the list
     */
    private int constructChildrenList(LinkedList hostList)
	throws ProtocolException
    {
	int curSize = hostList.size();
	Host curHost = null;
	int curIndex = 0;
	DDoSSessionRand curSess = null;
	children = new int[degree];

	if (DEBUG && level == MASTER)
	    System.out.println("Init Master "+((Host)inGraph()).nhi+"\tIP:"
			       +IP_s.IPtoString(ipSess.INTERFACE_SET[0].ipAddr));
	// pick *degree* number of DDoSHosts as children.
	for (int i = 0; (i<degree && curSize>0); i++) {
	    curIndex = new Double(rng.nextDouble() * curSize).intValue();
	    curHost = (Host)hostList.get(curIndex);

	    // remove it from the list
	    hostList.remove(curIndex);
	    curSize--;

	    try {
		// DDoSSession use "test" as name!!!!!
		curSess=(DDoSSessionRand)curHost.SessionForName("test");
	    } catch (ProtocolException pe) {
		//it's a host without DDoSSession
		i--;   //step back
		curSess = null;
		if (curSize <= 0) {
		    System.err.println("Warning: The number of DDoSHosts is not enough to establish control tree as defined.");

		    //re-group the childre array
		    int[] tArray = children;
		    children = new int[i+1];
		    System.arraycopy(tArray, 0, children, 0, i+1);		}
	    }

	    if (curSess == null)
		continue;

	    //it's a host with DDoSSession
	    //add it to children list
	    curSess.ipSess = (IP)(curHost.SessionForName("ip"));

	    children[i] = curSess.ipSess.INTERFACE_SET[0].ipAddr;

	    //initialize this master session.
	    if (level == ATTACKER) {
		curSess.level = MASTER;
		curSize = curSess.constructChildrenList(hostList);
	    }
	    else curSess.level = AGENT;
	}

	return curSize;
    }


    protected void initMaster(Configuration cfg) throws configException
    {
    }
    protected void initAgent(Configuration cfg) throws configException
    {
    }

    @Override
	public boolean push(ProtocolMessage message,
			ProtocolSession fromSession)
	throws ProtocolException
    {
	DDoSMessage msg = null;
	try {
	    msg = (DDoSMessage)message;
	} catch (ClassCastException cce) {
	    cce.printStackTrace();
	    throw new ProtocolException("Invalid message received in DDoSSession.push()");
	}

	// send back ACK msg if it's not an ACK itself
	if (msg.action != DDoSMessage.ACK) 
	    sendACK(msg);

	// monitoring
	if (monitorON) {
	    //for debug
	    System.err.println("DDoSMonitor got msg."+" "+msg.action);
	    monitor.receive(msg, fromSession, null);
	}

	// execute command
	switch (msg.action) {
	case DDoSMessage.ACK:
	    break;
	case DDoSMessage.HIJACK:
	    if (status == CLEAN)
		beingHijacked(msg, children);
	    else System.err.println("Warning: receive HIJACK cmd when status is not CLEAN. Host:"+((Host)inGraph()).nhi);
	    break;
	case DDoSMessage.START_ATTACK:
	    if (status == INSTALLED)
		startAttack(msg);
	    else System.err.println("Warning: receive START_ATTACK cmd when status is not INSTALLED.   Host:"+((Host)inGraph()).nhi);
	    break;
	case DDoSMessage.END_ATTACK:
	    if (status == ATTACKING)
		endAttack(msg);
	    else System.err.println("Warning: receive END_ATTACK cmd when status is not ATTACKING.   Host:"+((Host)inGraph()).nhi);
	    break;
	case DDoSMessage.INSTALL:
	    if (status == COMPROMISED)
		install(msg);
	    else System.err.println("Warning: receive INSTALL cmd when status is not NOT_INSTALLED.   Host:"+((Host)inGraph()).nhi);
	    break;
	case DDoSMessage.UNINSTALL:
	    if (status == ATTACKING)
		endAttack(msg);
	    if (status == INSTALLED)
		status = COMPROMISED;
	    else System.err.println("Warning: receive NOT_INSTALL cmd when status is not ATTACKING or INSTALLED.  Host:"+((Host)inGraph()).nhi);
	    break;
	default: {
		System.err.println("Invalid command:"+ msg.action+" in DDoSSession.push()");
		throw new ProtocolException("Invalide command in DDoSMessage");
	    }
	}
	return true;
    }

    protected void beingHijacked(DDoSMessage msg, final int[] toBeHijacked)
	throws ProtocolException
    {
	//In this version, there is NO downloading software process.
	//So receiving HIJACK cmd will turn the status into "INSTALLED"
	//instead of "COMPROMISED"

	//here "msg" is not really used.

	status = INSTALLED;
	IpHeader tmpHeader = null;

	if (toBeHijacked == null)
	    return;

	// send the hijack cmd to the hosts in the given list.
	// when hijacking machines, real IP addresses are used.
	/*	for (int i = 0; i < toBeHijacked.length; i++) {
	    tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
				     ipSess.INTERFACE_SET[0].ipAddr,
				     toBeHijacked[i]);
	    tmpHeader.carryPayload
		(new DDoSMessage(DDoSMessage.HIJACK, 0, 0, 0, 0));
	    ipSess.push(tmpHeader, this);
	}
	*/
	myTimer = new Timer
	    (inGraph(), SSF.Net.Net.seconds(TIME_COST)) {
		@Override
		public void callback() {
		    hijack(toBeHijacked);
		}
	    };
	myTimer.set();
    }

    protected void install(DDoSMessage msg)
    {
	status = INSTALLED;
    }

    protected void sendACK(DDoSMessage msg) throws ProtocolException
    {
	DDoSMessage ackMsg;

	// make ACK packet
	if (!spoof || msg.action == DDoSMessage.HIJACK)
	    ackMsg = new 
		DDoSMessage(DDoSMessage.ACK,0,0,msg.attackMethod,msg.param);
	else ackMsg = new
	    DDoSMessage(DDoSMessage.ACK,0,0,msg.attackMethod,msg.param,
			ipSess.INTERFACE_SET[0].ipAddr);
	int ackIP;
	IpHeader ipHeader;

	// get srcIP of the incoming msg
	if (!spoof || msg.action == DDoSMessage.HIJACK)
	    ackIP = ((IpHeader)(msg.previous())).SOURCE_IP;
	else ackIP = msg.opt;   //spoofed packets encode srcIP here.

	ipHeader = 
	    new IpHeader(Protocols.TEST_PRTL_NUM,
			 ipSess.INTERFACE_SET[0].ipAddr,ackIP);
	
	ipHeader.carryPayload(ackMsg);
	ipSess.push(ipHeader, this);
    }


    /** the next machine to be hijacked. */
    private int nextHijack=0;

    /** time "cost" in seconds to hijack a machine */
    protected int TIME_COST = 1;
    
    private void hijack(int[] toBeHijacked)
    {
	IpHeader tmpHeader = null;

	try {
	    tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
				     ipSess.INTERFACE_SET[0].ipAddr,
				     toBeHijacked[nextHijack]);
	    tmpHeader.carryPayload
		(new DDoSMessage(DDoSMessage.HIJACK, 0, 0, 0, 0));
	    ipSess.push(tmpHeader, this);
	} catch (ProtocolException pe) {
	    System.err.println(pe.getMessage());
	    pe.printStackTrace();
	    System.exit(-1);
	}

	nextHijack++;
	if (nextHijack == toBeHijacked.length) {
	    //finished 
	    myTimer.cancel();
	    myTimer = null;

	    if (level == ATTACKER) {
		//start attack timer
		if (DEBUG)
		    System.out.println("start Attacking timer.");
		
		myTimer = new Timer
		    (inGraph(), SSF.Net.Net.seconds(startAttackTime)-
		     inGraph().now()) {
			@Override
			public void callback() {
			    startAttack(null);
			}
		    };
		myTimer.set();
	    }
	} else myTimer.set();
    }

    protected void startHijack(final int[] toBeHijacked)
    {
	//IpHeader tmpHeader = null;

	if (DEBUG)
	    System.out.println("Start Hijacking ...");
	/*	try {
	    // send the hijack cmd to the hosts in the given list.
	    for (int i = 0; i < toBeHijacked.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 ipSess.INTERFACE_SET[0].ipAddr,
					 toBeHijacked[i]);
		tmpHeader.carryPayload
		    (new DDoSMessage(DDoSMessage.HIJACK, 0, 0, 0, 0));
		ipSess.push(tmpHeader, this);
	    }	
	} catch (ProtocolException pe) {
	    System.err.println(pe.getMessage());
	    pe.printStackTrace();
	    System.exit(-1);
	}
	*/
	//send msg to monitor
	if (monitorON) {
	    monitor.receive(new DDoSMessage(DDoSMessage.INSTALL, targetIP, targetPort, attackMethod, attackParam, 0), null, null);
	}
	// set timer to hijack
	myTimer = new Timer
	    (inGraph(), SSF.Net.Net.seconds(TIME_COST)) {
		@Override
		public void callback() {
		    hijack(toBeHijacked);
		}
	    };
	myTimer.set();
    }

    protected void startAttack(DDoSMessage msg)
    {
	status = ATTACKING;
	try {
	    switch (level) {
	    case ATTACKER:
		a_startAttack(msg);
		break;
	    case MASTER:
		m_startAttack(msg);
		break;
	    case AGENT:
		z_startAttack(msg);
		break;
	    default: {
		System.err.println("Host "+ ((Host)inGraph()).nhi+": Invalid level in DDoSSession.startAttack()");
		status = INSTALLED;
		}
	    }
	} catch (ProtocolException pe) {
	    System.err.println(pe.getMessage());
	    pe.printStackTrace();
	}
    }

    /** attacker start attack */
    private  void a_startAttack(DDoSMessage msg) throws ProtocolException
    {
	//create attacking command package.
	if (DEBUG)
	    System.out.println("--*-- Attacker start attacking --*-- "+
			       "\n\ttargetIP:"+IP_s.IPtoString(targetIP)+
			       "\n\ttargetPort:"+targetPort+
			       "\n\tattackMethod:"+attackMethod+
			       "\n\tattackParam:"+attackParam);
	IpHeader tmpHeader=null;

	//send msgs to all the masters one by one.
	if (!spoof) {
	    for (int i=0; i<children.length; i++) {
		//create an IP header to use.
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 ipSess.INTERFACE_SET[0].ipAddr,
					 children[i]);
		tmpHeader.carryPayload(new
		    DDoSMessage(DDoSMessage.START_ATTACK,
				targetIP,
				targetPort,
				attackMethod,
				attackParam));
		ipSess.push(tmpHeader, this);
	    }
	} else {
	    // use spoofed ip
	    for (int i = 0; i < children.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 randomIP(),
					 children[i]);
		tmpHeader.carryPayload
		    (new DDoSMessage(DDoSMessage.START_ATTACK,
				     targetIP,
				     targetPort,
				     attackMethod,
				     attackParam,
				     ipSess.INTERFACE_SET[0].ipAddr));
		ipSess.push(tmpHeader, this);
	    }
	}
	//set timer to end attack
	myTimer = new Timer(inGraph(), SSF.Net.Net.seconds(endAttackTime-startAttackTime)) 
	    {
		@Override
		public void callback() {
		    endAttack(null);
		}
	    };
	myTimer.set();
    }

    /** master start attack*/
    private void m_startAttack(DDoSMessage msg) throws ProtocolException
    {
	if (DEBUG)
	    System.out.println("master "+((Host)inGraph()).nhi+" start attacking.");
	IpHeader tmpHeader;
	if (!spoof) {
	    for (int i=0; i<children.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 ipSess.INTERFACE_SET[0].ipAddr,
					 children[i]);
		tmpHeader.carryPayload(new DDoSMessage(msg));
		ipSess.push(tmpHeader, this);
	    }
	} else {
	    //spoof packets
	    for (int i=0; i<children.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 randomIP(),
					 children[i]);
		tmpHeader.carryPayload(new 
		    DDoSMessage(msg, ipSess.INTERFACE_SET[0].ipAddr));
		ipSess.push(tmpHeader, this);
	    }
	}
    }

    /** zombie start attack*/
    private void z_startAttack(DDoSMessage msg) throws ProtocolException
    {
	// get all parameters.
	targetIP = msg.targetIP;
	targetPort = msg.targetPort;
	attackParam = msg.param;
	attackMethod = msg.attackMethod;

	if (DEBUG)
	    System.out.println("zombie:"+((Host)inGraph()).nhi+
			       " intializing attacking.");

	if (attackMethod != SYN) {
	    System.err.println("attacking method other than \"SYN\" hasn't been implemented.");
	    throw new ProtocolException("attacking method not implemented.");
	}

	// start the timer for attacking. 
	myTimer = new Timer(inGraph(), 1) {
		@Override
		public void callback(){
		    zombieAttack();
		}
	    };
	myTimer.set(attackParam * SSF.Net.Net.seconds(1));
    }

    private void zombieAttack()
    {
	if (DEBUG)
	    System.out.println("Agent:"+((Host)inGraph()).nhi+" is sending out attacking packet.");

	//create the TCP Header part.
	TCP_Header tcpHeader = new TCP_Header
	    ((new Double(rng.nextDouble())).intValue(), 
	     targetPort,0,0,30,TCP_Header.SYN);

	TCP_Message outMsg = new TCP_Message
	    (tcpHeader, randomIP(), targetIP,1);


	try {
	    ipSess.push(outMsg.previous(), this);
	} catch (ProtocolException pe) {
	    pe.printStackTrace();
	    System.err.println(pe.getMessage());
	}

	myTimer.set(attackParam * SSF.Net.Net.seconds(1));
    }

    protected void endAttack(DDoSMessage msg)
    {
	//in this implementation, "msg" is not used.
	status = INSTALLED;
	try {
	    switch (level) {
	    case ATTACKER:
		a_endAttack(null);
		break;
	    case MASTER:
		m_endAttack(msg);
		break;
	    case AGENT:
		z_endAttack(msg);
		break;
	    default:
		System.err.println("Host " + ((Host)inGraph()).nhi+ ": Invalid level in DDoSLevel.endAttack(). Level:"+level);
	    }
	} catch (ProtocolException pe) {
	    System.err.print(pe.getMessage());
	}
    }

    private void a_endAttack(DDoSMessage msg) throws ProtocolException
    {
	// stop the timer.
	if (DEBUG)
	    System.out.println("Attacker issue stop attacking command.");
	myTimer = null;

	IpHeader tmpHeader=null;

	//send msgs to all the masters one by one.
	if (!spoof) {
	    for (int i=0; i<children.length; i++) {
		//create an IP header to use.
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 ipSess.INTERFACE_SET[0].ipAddr,
					 children[i]);
		tmpHeader.carryPayload(new 
		    DDoSMessage(DDoSMessage.END_ATTACK,
				0,0,0,0,0));
		ipSess.push(tmpHeader, this);
	    }
	} else {
	    // use spoofed packets
	    for (int i = 0; i < children.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 randomIP(),
					 children[i]);
		tmpHeader.carryPayload(new
		    DDoSMessage(DDoSMessage.END_ATTACK,
				0,0,0,0,
				ipSess.INTERFACE_SET[0].ipAddr));
		ipSess.push(tmpHeader, this);
	    }
	}
	// send out stop message.

    }

    private void m_endAttack(DDoSMessage msg) throws ProtocolException
    {
	// just forward msg
	if (DEBUG)
	    System.out.println("Master "+ ((Host)inGraph()).nhi+" forwards stop attacking command.");
	IpHeader tmpHeader;
	if (!spoof) {
	    for (int i=0; i<children.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 ipSess.INTERFACE_SET[0].ipAddr,
					 children[i]);
		tmpHeader.carryPayload(new DDoSMessage(msg));
		ipSess.push(tmpHeader, this);
	    }
	} else {
	    //spoof packets
	    for (int i=0; i<children.length; i++) {
		tmpHeader = new IpHeader(Protocols.TEST_PRTL_NUM,
					 randomIP(),
					 children[i]);
		tmpHeader.carryPayload(new 
		    DDoSMessage(msg, ipSess.INTERFACE_SET[0].ipAddr));
		ipSess.push(tmpHeader, this);
	    }
	}
    }

    private void z_endAttack(DDoSMessage msg) throws ProtocolException
    {
	// stop the timer
	if (DEBUG)
	    System.out.println("Agent:"+((Host)inGraph()).nhi+" stop attacking at "+inGraph().now()/(float)SSF.Net.Net.seconds(1));
	myTimer.cancel();
	myTimer = null;
    }

    private int randomIP()
    {
	return (new Double(rng.nextDouble()*Integer.MAX_VALUE).intValue());
    }
}








